/* ****************************************************************
   ****************************************************************
   * Com++ Professional (c) 2009 C++ World
   * http://www.cplusplusworld.com/ mailto:compp@cplusplusworld.com
   ****************************************************************
   ****************************************************************
*/

#ifndef _SIGNALLING_H
#define _SIGNALLING_H

#include <list>
#include <iterator>
#include <map>
#include "ComPlusPlus/ComPlusPlus"


namespace compp {

  typedef uint32_t SignallingMemberID;

  template <class TT>
  class SignallingMemberList  {
  public:
    SignallingMemberList ()          {  MItems = new std::list<TT * >; MMutex = new compp::Mutex; MSem = new compp::Sem; }
    virtual ~SignallingMemberList () {  MItems->clear(); delete MItems ; delete MMutex; delete MSem;  }
    std::list <TT * > * MItems;
    compp::Mutex   * MMutex;
    compp::Sem     * MSem;
  };

  template <class T>
    class Signalling {
  protected:

    compp::Mutex SignallingMutex;
    SignallingMemberID MaxMemberID;
    typedef SignallingMemberList<T> * SignalMemberT;

    std::map<const uint32_t, SignallingMemberList<T> *  > ItemList;

  public:
    Signalling() {MaxMemberID = 0 ;}
    virtual ~Signalling() {}

    void Post ( SignallingMemberID ID, T Item ) {

      typename std::map<const uint32_t, SignallingMemberList<T> *  >::iterator Irritator ;

      compp::CriticalSection CS (SignallingMutex); 
      Irritator = ItemList.begin();

      while ( Irritator != ItemList.end() ) {
	
	if ( Irritator->first != ID ) {

	  Irritator->second->MMutex->Lock();
	  T *tmpT =  new T;
	  *tmpT = Item;
	  Irritator->second->MItems->push_back ( tmpT );
	  Irritator->second->MSem->Post();
	  Irritator->second->MMutex->UnLock();
	}
	++Irritator;
      }
    }

    void Wait ( SignallingMemberID ID, T &  Item ) {

      typename std::map<const uint32_t, SignallingMemberList<T> *  >::iterator Irritator  ;

    T *retval;
    {
      compp::CriticalSection CS (SignallingMutex);
      Irritator = ItemList.find (ID) ;
      
      if ( Irritator == ItemList.end() ) {
	throw syspp::ComException ( "Signalling Subscription does not exist." );      
      }
    }
    Irritator->second->MSem->Wait();  // Blocking
    Irritator->second->MMutex->Lock();
    retval = Irritator->second->MItems->front ();
    Item = *retval;
    delete retval;
    Irritator->second->MItems->pop_front ();
    Irritator->second->MMutex->UnLock();
    
  }

  bool TryWait ( SignallingMemberID ID, T & Item ) {

    T *retval;

/*     typename std::map<const uint32_t, SignallingMemberList<T> *  >::iterator Irritator = ItemList.find (ID) ; */
    typename std::map<const uint32_t, SignallingMemberList<T> *  >::iterator Irritator  ;
    {
      compp::CriticalSection CS (SignallingMutex);
      Irritator = ItemList.find (ID) ;

      if ( Irritator == ItemList.end() ) {
	throw syspp::ComException ( "Signalling Subscription does not exist." );      
      }

      if ( false == Irritator->second->MSem->TryWait() ) {
	return false;
      }
    }
    Irritator->second->MMutex->Lock();
    retval = Irritator->second->MItems->front ();
    Item = *retval;
    delete retval;
    Irritator->second->MItems->pop_front ();
    Irritator->second->MMutex->UnLock();

    return true;
  }

  SignallingMemberID Join () {

    SignallingMemberID RetVal;
    SignallingMemberList<T> *tmp ;    

    tmp = new SignallingMemberList<T>;

    compp::CriticalSection CS (SignallingMutex);

    RetVal = MaxMemberID++;
    ItemList[RetVal] = tmp;
    
    return RetVal;
  }

  void Leave (SignallingMemberID ID) {

    compp::CriticalSection CS (SignallingMutex);
    typename std::map<const uint32_t, SignallingMemberList<T> *  >::iterator Irritator = ItemList.find (ID) ;
    if ( Irritator == ItemList.end() ) {
      throw syspp::ComException ( "Cannot cancel a non exsiting Signalling Subscription." );      
    }
    delete Irritator->second;
    ItemList.erase ( Irritator );
  }
 };
}
#endif
